﻿using SQ.BLL.Interfaces;
using SQ.Domain;
using SQ.Infrastructure.DataContexts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SQ.BLL.Implementation
{
    public class FilaBS : IFilaBS
    {
        private SmartQueueDataContext data = new SmartQueueDataContext();

        public List<Fila> ListarFilasPorEstabelecimento(int codigoEstabelecimento)
        {
            try
            {
                if (codigoEstabelecimento == 0)
                {
                    throw new Exception("Estabelecimento não informado");
                }

                return data.Filas.Where(p => p.Id == codigoEstabelecimento).ToList();
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Erro ao buscar filas por estabelecimento, motivo: {0}", ex.Message));
            }
        }

        public Fila ObterFilaAberta(int codigoEstabelecimento)
        {
            try
            {
                Fila filaAberta = null;
                if (codigoEstabelecimento == 0)
                {
                    throw new Exception("Estabelecimento não informado");
                }

                filaAberta = data.Filas.FirstOrDefault(p => (p.DataFimFila == null || p.DataFimFila == DateTime.MinValue) && p.Estabelecimento.Id == codigoEstabelecimento);

                if (filaAberta == null)
                {
                    Estabelecimento estabelecimento = this.data.Estabelecimentos.FirstOrDefault(p => p.Id == codigoEstabelecimento);

                    filaAberta = AbrirFila(estabelecimento);
                }

                return filaAberta;
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Erro ao buscar filas abertas, motivo: {0}", ex.Message));
            }
        }

        public Fila ObterFilaPorUsuario(int codigoUsuario)
        {
            try
            {
                if (codigoUsuario == 0)
                {
                    throw new Exception("Usuário não informado");
                }

                var integrante = data.IntegrantesFila.FirstOrDefault(a => a.Usuario.Id == codigoUsuario && (a.DataSaidaFila == null || a.DataSaidaFila == DateTime.MinValue));

                if (integrante == null)
                {
                    return null;
                }
                else
                {
                    return integrante.Fila;
                }
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Erro ao buscar fila do usuário, motivo: {0}", ex.Message));
            }
        }

        public void AdicionarIntegranteFila(IntegranteFila integrante)
        {
            try
            {
                if (integrante.IsValid())
                {
                    if (UsuarioAindaNaoEstaNaFila(integrante))
                    {
                        Fila filaInformada = this.data.Filas.FirstOrDefault(p => p.Id == integrante.Fila.Id);

                        integrante.Fila = filaInformada;
                        integrante.FilaId = filaInformada.Id;

                        Usuario userFila = this.data.Usuarios.FirstOrDefault(p => p.Id == integrante.Usuario.Id);

                        integrante.UsuarioId = userFila.Id;
                        integrante.Usuario = userFila;

                        data.IntegrantesFila.Add(integrante);
                        data.SaveChanges();
                    }
                    else
                    {
                        throw new ArgumentException("Usuário já está na fila");
                    }
                }
                else
                {
                    throw new ArgumentException("Integrante inválido");
                }
            }
            catch (ArgumentException ex)
            {
                throw ex;
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Erro ao adicionar usuário na fila, motivo: {0}", ex.Message));
            }
        }

        private bool UsuarioAindaNaoEstaNaFila(IntegranteFila integrante)
        {
            var integranteJaNaFila = this.data.IntegrantesFila.Where(p => p.Usuario.Id == integrante.Usuario.Id
                                                                    && p.Fila.Id == integrante.Fila.Id).ToList();

            var countDataNula = integranteJaNaFila.Count(p => p.DataSaidaFila == DateTime.MinValue || p.DataSaidaFila == null);

            return countDataNula == 0;

        }

        public List<Fila> GetFilasAbertas()
        {
            try
            {
                return data.Filas.Where(p => p.DataFimFila == null || p.DataFimFila == DateTime.MinValue).ToList();
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Erro ao buscar filas, motivo: {0}", ex.Message));
            }

        }


        public void FecharFila(Fila fila)
        {
            try
            {
                fila.EstabelecimentoId = fila.Estabelecimento.Id;
                fila.DataFimFila = DateTime.Now;

                this.data.Entry(fila).State = System.Data.Entity.EntityState.Modified;
                this.data.SaveChanges();
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Erro ao buscar fechar fila, motivo: {0}", ex.Message));
            }
        }

        public Fila AbrirFila(Estabelecimento estabelecimento)
        {
            try
            {
                Fila novaFila = new Fila()
                {
                    DataFimFila = DateTime.MinValue,
                    DataInicioFila = DateTime.Now,
                    Estabelecimento = estabelecimento,
                    EstabelecimentoId = estabelecimento.Id,
                    Id = 0,
                    TempoEsperaMedio = 0
                };

                this.data.Filas.Add(novaFila);
                this.data.SaveChanges();

                return novaFila;
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Erro ao buscar fechar fila, motivo: {0}", ex.Message));
            }
        }

        public void AtualizarFila(Fila fila)
        {
            try
            {
                this.data.Entry(fila).State = System.Data.Entity.EntityState.Modified;
                this.data.SaveChanges();
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Erro ao buscar fechar fila, motivo: {0}", ex.Message));
            }
        }
    }
}
